package org.budo.mongo.jdbc.driver.statement;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.bson.types.ObjectId;
import org.budo.jdbc.driver.AbstractBudoJdbcPreparedStatement;
import org.budo.jdbc.driver.exception.BudoJdbcDriverNotSupportedException;
import org.budo.support.lang.util.StringUtil;

/**
 * @author lmw
 */
public class ParameterProcessor {
    private String processString(String parameterValue) {
        if (null == parameterValue || parameterValue.trim().isEmpty()) {
            return parameterValue;
        }

        return parameterValue.replace("\"", "\\\"");
    }

    /**
     * 将BasicDBObject中的特殊的参数值(如Date)替换为实际值
     * 
     * @param value 经过初次处理的参数
     */
    public Object replaceParameterValue(Object value, List<Date> parameterTemp) {
        if (null == value || null == parameterTemp || parameterTemp.isEmpty()) {
            return value;
        }

        if (value instanceof Number || value instanceof Boolean) {
            return value; // 返回空或自己，不会替换
        }

        if (value instanceof String) {
            String stringValue = (String) value;
            if (stringValue.startsWith(MongoStatement.DATE_PREFIX)) {
                String _index = stringValue.substring(MongoStatement.DATE_FROM_INDEX, MongoStatement.DATE_TO_INDEX);
                int index = new Integer(_index);
                Date date = parameterTemp.get(index); // parameterTemp.remove(index); // 移除这个
                return date; // 返回
            }

            return value;
        }

        if (value instanceof Map) {
            Map<String, Object> map = (Map) value;
            Set<Entry<String, Object>> entrySet = map.entrySet();
            for (Entry<String, Object> entry : entrySet) {
                String key = entry.getKey();
                Object val = entry.getValue();

                // ID 类型参数处理
                String val_str = StringUtil.toString(val);
                if (MongoStatement._ID.equals(key) && StringUtil.isNotNullOrEmpty(val_str) && ObjectId.isValid(val_str)) {
                    entry.setValue(new ObjectId(val_str));
                    continue;
                }

                Object replacement = this.replaceParameterValue(val, parameterTemp); // 递归
                if (null != replacement && !replacement.equals(val) && replacement instanceof Date) { // 返回了一个 Date
                    entry.setValue(replacement); // 替换值为 Date
                    continue;
                }
            }

            return map;
        }

        if (value instanceof List) {
            List<Object> list = (List) value;
            for (int i = 0; i < list.size(); i++) {
                Object eachValue = list.get(i);
                Object replacement = replaceParameterValue(eachValue, parameterTemp); // 递归
                if (null != replacement && !replacement.equals(eachValue) && replacement instanceof Date) { // 返回了一个 Date
                    list.set(i, replacement); // 替换值为 Date
                }
            }

            return list;
        }

        throw new RuntimeException("#167 value=" + value + ", type=" + value.getClass());
    }

    /**
     * 将statementJson替换参数后返回，时间类型后面再处理
     */
    public String statementParameterize(String statementJson, Map<Integer, Object> parameterMap, List<Date> parameterTemp) {
        if (null == statementJson || statementJson.isEmpty()) {
            return statementJson;
        }

        String statementTemp = new String(statementJson);

        int fromIndex = 0; // 上一次while循环已扫描到这个位置
        while (true) {
            int beginIndex = statementTemp.indexOf(MongoStatement.PARAMETER_PREFIX, fromIndex);
            if (beginIndex < 0) {
                break; // 结束了
            }

            int endIndex = statementTemp.indexOf(MongoStatement.PARAMETER_SUFFIX, beginIndex + 1);
            String jdbcParameterIndex = statementTemp.substring(beginIndex + MongoStatement.PARAMETER_PREFIX_LENGTH, endIndex);
            Integer parameterIndex = new Integer(jdbcParameterIndex);
            Object parameterValue = parameterMap.get(parameterIndex);

            if (null == parameterValue) {
                throw new RuntimeException("#198 No value specified for parameter " + parameterIndex);
            }

            if (AbstractBudoJdbcPreparedStatement.NULL.equals(parameterValue) || parameterValue instanceof Number || parameterValue instanceof Boolean) {
                statementTemp = statementTemp.substring(0, beginIndex - 1) + parameterValue + statementTemp.substring(endIndex + 3); // 无引号

                // 记录本轮已处理到的位置，下一轮从这里往后继续处理，因为替换过参数，长度有变化，所以不能从 endIndex开始
                fromIndex = beginIndex;
                continue;
            }

            if (parameterValue instanceof String) {
                String processString = processString((String) parameterValue);
                statementTemp = statementTemp.substring(0, beginIndex) + processString + statementTemp.substring(endIndex + 2); // 有引号

                fromIndex = beginIndex;
                continue;
            }

            if (parameterValue instanceof Date) {
                Integer parameterTempIndex = parameterTemp.size(); //
                if (parameterTempIndex >= MongoStatement.DATE_COUNT_MAX) { // 最多允许DATE_COUNT_MAX个Date参数
                    throw new BudoJdbcDriverNotSupportedException("#206 Date 类型参数个数超过" + MongoStatement.DATE_COUNT_MAX //
                            + " , parameterValue=" + parameterValue + ", parameterValue.type=" + parameterValue.getClass());
                }

                parameterTemp.add((Date) parameterValue); //

                // 转为 DATE_DIGIT 位，不够在前面补0
                String parameterPlaceholder = MongoStatement.DATE_PREFIX //
                        + String.format(MongoStatement.DATE_INDEX_FORMAT, parameterTempIndex) //
                        + MongoStatement.DATE_SUFFIX;

                // 先字符串占位,转成BasicDBObject对象后再替换
                statementTemp = statementTemp.substring(0, beginIndex) + parameterPlaceholder + statementTemp.substring(endIndex + 2);

                fromIndex = beginIndex;
                continue;
            }

            if (parameterValue instanceof Date[]) {
                throw new RuntimeException("#212 不支持 Date[] 数组, parameterValue=" + parameterValue);
            }

            throw new BudoJdbcDriverNotSupportedException("#214 parameterValue=" + parameterValue + ", parameterValue.type=" + parameterValue.getClass());
        }

        return statementTemp;
    }
}